package cn.com.infcn.monitor.secure;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class HmacMaker {

	public final static String HMAC_MD5		= "HmacMD5";
	public final static String HAMC_SHA1	= "HmacSHA1";
	
	private Mac hmac;
	private SecretKey secretkey;
	private String algorithm = HMAC_MD5;
	
	/**
	 * Use the default algorithm HMAC_MD5
	 * @param key shared secret key
	 * @throws IllegalArgumentException
	 */
	public HmacMaker(String key) throws IllegalArgumentException {
		this(key.getBytes(), HMAC_MD5);
	}
	
	/**
	 * Use the default algorithm HMAC_MD5
	 * @param key shared secret key
	 * @throws IllegalArgumentException
	 */
	public HmacMaker(byte[] key) throws IllegalArgumentException {
		this(key, HMAC_MD5);
	}
	
	/**
	 * Constructor
	 * @param key shared secret key
	 * @param alg hash algorithm: HMAC_MD5 or HMAC_SHA1
	 * @throws IllegalArgumentException
	 */
	public HmacMaker(byte[] key, String alg) throws IllegalArgumentException {
		if (alg == null || !(alg.equals(HMAC_MD5) || alg.equals(HAMC_SHA1)))
			throw new IllegalArgumentException("Algorithm shall be HmacMD5 or HmacSHA1");
		if (key == null || key.equals(""))
			throw new IllegalArgumentException("Secret key shall NOT be empty");
		
		algorithm = alg;
		secretkey = new SecretKeySpec(key, algorithm);
		
		try {
			hmac = Mac.getInstance(algorithm);
			hmac.init(secretkey);
		} catch (NoSuchAlgorithmException e) {
			// do nothing
			throw new IllegalArgumentException(e);
		} catch (InvalidKeyException e) {
			// do nothing
			throw new IllegalArgumentException(e);
		}
	}
	
	/**
	 * Generate HMAC digest for message
	 * @param message String
	 * @return hex string
	 */
	public String hexDigest(String message) {
		return hexDigest(message.getBytes());
	}

	/**
	 * Generate HMAC digest for message
	 * @param message byte[]
	 * @return hex string
	 */
	public String hexDigest(byte[] message) {
		byte[] d = digest(message);
		if (d == null || d.length == 0)
			return null;
		
		Formatter formatter = new Formatter();
		for (int i=0; i<d.length; i++) {
			formatter.format("%02x", d[i]);
		}
		String result = formatter.toString();
		formatter.close();
		return result; 
	}
	
	/**
	 * Generate HMAC digest for message
	 * @param message String
	 * @return byte[]
	 */
	public byte[] digest(String message) {
		assert(message != null && message.length() > 0);
		
		try {
			Mac mac = (Mac)hmac.clone();
			mac.update(message.getBytes());
			return mac.doFinal();
		} catch (CloneNotSupportedException e) {
			// do nothing
			return null;
		}
	}

	/**
	 * Generate HMAC digest for message
	 * @param message byte[]
	 * @return byte[]
	 */
	public byte[] digest(byte[] message) {
		assert(message != null && message.length > 0);
		
		try {
			// For thread safe
			Mac mac = (Mac)hmac.clone();
			mac.update(message);
			return mac.doFinal();
		} catch (CloneNotSupportedException e) {
			try {
				Mac mac = Mac.getInstance(algorithm);
				mac.init(secretkey);
				mac.update(message);
				return mac.doFinal(); 
			} catch (Exception e1) {
				e1.printStackTrace();
				return null;
			}
		}
	}
}
